'use strict'

const logCommand = ({ options, originalOptions }) => {
  if (options.log) {
    options.logger({
      name: options.description,
      message: options.customMessage,
      consoleProps: () => originalOptions,
    })
  }
}
const logCommandCheck = ({ result, options, originalOptions }) => {
  if (!options.log || !options.verbose) return

  const message = [result]
  if (options.customCheckMessage) {
    message.unshift(options.customCheckMessage)
  }
  options.logger({
    name: options.description,
    message,
    consoleProps: () => originalOptions,
  })
}

const waitUntil = (subject, checkFunction, originalOptions = {}) => {
  if (!(checkFunction instanceof Function)) {
    throw new Error('`checkFunction` parameter should be a function. Found: ' + checkFunction)
  }

  const defaultOptions = {
    // base options
    interval: 200,
    timeout: 5000,
    errorMsg: 'Timed out retrying',

    // log options
    description: 'waitUntil',
    log: true,
    customMessage: undefined,
    logger: Cypress.log,
    verbose: false,
    customCheckMessage: undefined,
  }
  const options = { ...defaultOptions, ...originalOptions }

  // filter out a falsy passed "customMessage" value
  options.customMessage = [options.customMessage, originalOptions].filter(Boolean)

  const endTime = Date.now() + options.timeout

  logCommand({ options, originalOptions })

  const check = (result) => {
    logCommandCheck({ result, options, originalOptions })
    if (result) {
      return result
    }
    if (Date.now() >= endTime) {
      const msg =
        options.errorMsg instanceof Function ? options.errorMsg(result, options) : options.errorMsg
      throw new Error(msg)
    }
    cy.wait(options.interval, { log: false }).then(() => {
      return resolveValue()
    })
  }

  const resolveValue = () => {
    const result = checkFunction(subject)

    const isAPromise = Boolean(result && result.then)
    if (isAPromise) {
      return result.then(check)
    } else {
      return check(result)
    }
  }

  return resolveValue()
}

Cypress.Commands.add('waitUntil', { prevSubject: 'optional' }, waitUntil)
